package server

import (
	"sync"
	"sync/atomic"
	"time"
	transCommon "ws-demo-go/internal/transport/common"
	"ws-demo-go/pkg/logger"
	"ws-demo-go/pkg/ws/common"
)

const (
	checkPingInterval = 5 * time.Second  // 检查客户端 Ping 帧的间隔
	pingTimeout       = 30 * time.Second // 等待 Ping 帧的超时阈值，若超过此时间未收到客户端 Ping 帧，则关闭连接
	sendChaneSize     = 200              // 发送缓冲队列的大小
)

type Connection struct {
	wsConn       *common.Conn                        // 底层 WebSocket 连接
	connID       string                              // 连接唯一标识
	sendChan     transCommon.SendChan                // 发送消息缓冲队列
	onMessage    func(conn *Connection, data []byte) // 收到消息的回调（交给上层处理）
	lastPingTime int64                               // 客户端最后一次心跳的时间戳
	closeChan    chan struct{}                       // 连接关闭通知通道
	closed       bool                                // 连接是否关闭
	mu           sync.Mutex                          // 保护 closed 状态
}

// NewConnection 创建连接实例
func NewConnection(wsConn *common.Conn, connID string) *Connection {
	return &Connection{
		wsConn:       wsConn,
		connID:       connID,
		sendChan:     make(transCommon.SendChan, sendChaneSize),
		closeChan:    make(chan struct{}),
		lastPingTime: time.Now().Unix(),
	}
}

// SendBytes 发送字节流数据
func (tc *Connection) SendBytes(data []byte) error {
	return transCommon.SendBytes(tc.sendChan, data)
}

// Close 关闭连接
func (tc *Connection) Close() {
	tc.mu.Lock()
	defer tc.mu.Unlock()
	if tc.closed {
		return
	}

	err := (*tc.wsConn).Close()
	if err != nil {
		logger.Error("Close error: %v", err)
		return
	}
	close(tc.sendChan)

	tc.closed = true
	close(tc.closeChan)
}

// IsClosed 检查连接是否关闭
func (tc *Connection) IsClosed() bool {
	tc.mu.Lock()
	defer tc.mu.Unlock()
	return tc.closed
}

// 检查 Ping 帧是否超时
func (tc *Connection) isPingTimeout() bool {
	lastPing := time.Unix(atomic.LoadInt64(&tc.lastPingTime), 0)
	return time.Since(lastPing) > pingTimeout
}

// GetConnID 获取连接ID
func (tc *Connection) GetConnID() string {
	return tc.connID
}

// 获取连接关闭通知通道
func (tc *Connection) getClosedChan() <-chan struct{} {
	return tc.closeChan
}

// 读循环：持续接收客户端消息，并通过 OnMessage 回调给上层
func (tc *Connection) readLoop() {
	for {
		frame, data, err := (*tc.wsConn).ReadFrame2Bytes(false)
		if err != nil {
			if !common.IsClosedErr(err) {
				logger.Error("Read error: %v", err)
			}
			tc.Close()
			return
		}
		switch frame.Opcode {
		case common.OpClose:
			// 如果是关闭帧，TODO 之后需要处理关闭帧

		case common.OpPing:
			// 如果是 Ping 帧（服务端只支持接收 Ping 帧），则刷新客户端连接的状态，并发送 Pong 帧
			tc.mu.Lock()
			tc.closed = false
			tc.mu.Unlock()
			err := transCommon.AddToSendChanWithoutBlocking(tc.sendChan, transCommon.PongFrame)
			if err != nil {
				logger.Error("Write to send chan error: %v", err)
			}

		case common.OpBinary:
			// 如果是数据帧，将消息传递给上层
			if onMessage := tc.onMessage; onMessage != nil {
				onMessage(tc, data)
			}

		default:
			logger.Error("Unknown opcode in server: %v", frame.Opcode)
		}
	}
}

// 写循环：从 sendChan 取消息并发送给客户端
func (tc *Connection) writeLoop() {
	for incompleteFrame := range tc.sendChan {
		err := (*tc.wsConn).WriteBytes2Frame(incompleteFrame.Opcode, incompleteFrame.Payload, false)
		if err != nil {
			logger.Error("Write error: %v", err)
			tc.Close()
			return
		}
	}
}

// 检测 Ping 帧循环
func (tc *Connection) checkPingLoop() {
	ticker := time.NewTicker(checkPingInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			if tc.isPingTimeout() {
				logger.Info("Client %s ping timeout, close the connection", tc.connID)
				tc.Close()
				return
			}
		case <-tc.closeChan:
			return
		}
	}
}
